package com.eastfair.venuebooking.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
//import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eastfair.boot.request.PageParams;
import com.eastfair.boot.service.SuperServiceImpl;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.context.StaticContextUtil;
import com.eastfair.core.exception.BizException;
import com.eastfair.core.exception.code.ExceptionCode;
import com.eastfair.messagecenter.dto.SmsSendDTO;
import com.eastfair.messagecenter.enumeration.ChannelType;
import com.eastfair.messagecenter.model.SmsContentModel;
import com.eastfair.venuebooking.dao.VenueBookAuditMapper;
import com.eastfair.venuebooking.dto.VenueBookAuditDTO;
import com.eastfair.venuebooking.entity.BusinessOpportunity;
import com.eastfair.venuebooking.entity.ContactRef;
import com.eastfair.venuebooking.entity.VenueBookAudit;
import com.eastfair.venuebooking.enumeration.VenueBookAuditAuditStatusEnum;
import com.eastfair.venuebooking.enumeration.VenueBookAuditVenueBookSourceEnum;
import com.eastfair.venuebooking.exceptioncode.VenueBookingExceptionCode;
import com.eastfair.venuebooking.service.*;
import com.eastfair.venuebooking.vo.VenueBookAuditVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 场馆预定审核表
 * </p>
 *
 * @author dqq
 * @date 2023-02-22
 */
@Slf4j
@Service
//@DS("#thread.tenant")
public class VenueBookAuditServiceImpl extends SuperServiceImpl<VenueBookAuditMapper, VenueBookAudit> implements VenueBookAuditService {

    @Resource
    private CustomerMainService customerMainService;

    @Resource
    private ContactService contactService;

    @Resource
    private ContactRefService contactRefService;

    @Resource
    private BusinessOpportunityService businessOpportunityService;

    @Resource
    private ExhibitionService exhibitionService;

    @Resource
    private VenueBookingMsgService venueBookingMsgService;

    @Resource
    private MeetAdService meetAdService;

    @Resource(name = "threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 处理逻辑删除相关处理
     *
     * @param modelList 实体
     * @return 是否成功
     */
    @Override
    protected R<Boolean> handlerRemoveByIdsOfLogic(Collection<VenueBookAudit> modelList) {
        modelList.forEach(s -> s.setIsDeleted(BusinessConstant.YES));
        return R.successDef();
    }

    @Override
    protected R<Boolean> handlerSave(VenueBookAudit model) {
        model.setProjectId(ContextUtil.getProjectId());
        model.setSubsystemId(ContextUtil.getSubSystemId());
        if (model.getAuditStatus() == null) {
            model.setAuditStatus(VenueBookAuditAuditStatusEnum.SUBMIT);
        }
        if (model.getSubmitTime() == null) {
            model.setSubmitTime(LocalDateTime.now());
        }
        return R.successDef();
    }

    @Override
    public Page<VenueBookAuditVO> listVenueBookAuditVO(PageParams<VenueBookAuditDTO> pageParams) {
        Page<VenueBookAudit> pageParam = pageParams.buildPage();
        VenueBookAuditDTO query = pageParams.getModel();
        QueryWrapper<VenueBookAudit> queryWrapper = createVenueBookAuditVOQueryWrapper(query);
        Page<VenueBookAudit> page = page(pageParam, queryWrapper);
        Page<VenueBookAuditVO> voPage = new Page<>();
        BeanUtil.copyProperties(page, voPage);
        List<VenueBookAudit> list = page.getRecords();
        if (list == null || list.isEmpty()) {
            return voPage;
        }
        List<VenueBookAuditVO> voList = list
                .stream()
                .map(venueBookAudit -> BeanUtil.toBean(venueBookAudit, VenueBookAuditVO.class))
                .collect(Collectors.toList());
        voPage.setRecords(voList);
        return voPage;
    }

    @Override
    public VenueBookAuditVO getVenueBookAuditVOById(Long id) {
        log.info("getVenueBookAuditVOById - id 查询详情, id={}", id);
        VenueBookAudit venueBookAudit = getById(id);
        if (venueBookAudit == null) {
            return null;
        }
        return BeanUtil.toBean(venueBookAudit, VenueBookAuditVO.class);
    }

    @Override
    public List<VenueBookAudit> listVenueBookAudit(VenueBookAuditDTO venueBookAuditDTO) {
        log.info("listVenueBookAudit - 查询场馆预定列表, venueBookAuditDTO={}", venueBookAuditDTO);
        QueryWrapper<VenueBookAudit> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(VenueBookAudit::getIsDeleted, BusinessConstant.DELETE_NO)
                .ne(VenueBookAudit::getAuditStatus, VenueBookAuditAuditStatusEnum.NOT_APPROVED.getCode())
                .eq(StrUtil.isNotBlank(venueBookAuditDTO.getExternalExhibitionCode()), VenueBookAudit::getExternalExhibitionCode, venueBookAuditDTO.getExternalExhibitionCode())
                .eq(StrUtil.isNotBlank(venueBookAuditDTO.getExhibitionName()), VenueBookAudit::getExhibitionName, venueBookAuditDTO.getExhibitionName())
                .eq(StrUtil.isNotBlank(venueBookAuditDTO.getExhibitionNo()), VenueBookAudit::getExhibitionNo, venueBookAuditDTO.getExhibitionNo())
        ;
        return list(queryWrapper);
    }

    @Override
    public VenueBookAudit getByExternalExhibitionId(String externalExhibitionCode) {
        log.info("getByExternalExhibitionId - 外部系统ID查询场馆预定申请, externalExhibitionId={}", externalExhibitionCode);
        VenueBookAuditDTO query = new VenueBookAuditDTO();
        query.setExternalExhibitionCode(externalExhibitionCode);
        List<VenueBookAudit> list = listVenueBookAudit(query);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public VenueBookAudit getByExhibitionName(String exhibitionName) {
        log.info("getByExhibitionName - 展会名称查询场馆预定申请, exhibitionName={}", exhibitionName);
        VenueBookAuditDTO query = new VenueBookAuditDTO();
        query.setExhibitionName(exhibitionName);
        List<VenueBookAudit> list = listVenueBookAudit(query);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    public VenueBookAudit getByExhibitionNo(String exhibitionNo) {
        log.info("getByExhibitionNo - 展会编号查询场地预定申请, exhibitionNo={}", exhibitionNo);
        VenueBookAuditDTO query = new VenueBookAuditDTO();
        query.setExhibitionNo(exhibitionNo);
        List<VenueBookAudit> list = listVenueBookAudit(query);
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveForVenueWebSite(VenueBookAuditDTO venueBookAuditDTO) {
        log.info("saveVenueBookAudit - 添加场馆预定审核, venueBookAuditDTO={}", venueBookAuditDTO);
        venueBookAuditDTO.setVenueBookSource(VenueBookAuditVenueBookSourceEnum.VENUE_WEBSITE);
        // 尝试创建生成展会，商机，客户，联系人信息，验证能否通过校验
        // trySaveExhibitionInfo(venueBookAuditDTO);
        return saveVenueBookAudit(venueBookAuditDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveForConfluence(VenueBookAuditDTO venueBookAuditDTO) {
        log.info("saveForConfluence - 外部展览会议系统保存场馆预定申请, venueBookAuditDTO={}", venueBookAuditDTO);
        venueBookAuditDTO.setVenueBookSource(VenueBookAuditVenueBookSourceEnum.CONFERENCE);
        return saveVenueBookAudit(venueBookAuditDTO);
    }

    @Override
    public boolean saveVenueBookAudit(VenueBookAuditDTO venueBookAuditDTO) {
        log.info("saveVenueBookAudit - 添加场馆预定审核, venueBookAuditDTO={}", venueBookAuditDTO);
        // 校验外部系统的展会ID不能重复
        if (StrUtil.isNotBlank(venueBookAuditDTO.getExternalExhibitionCode())) {
            VenueBookAudit existByExternalExhibitionCode = getByExternalExhibitionId(venueBookAuditDTO.getExternalExhibitionCode());
            if (existByExternalExhibitionCode != null) {
                throw BizException.wrap(VenueBookingExceptionCode.VENUE_BOOK_EXTERNAL_EXHIBITION_CODE_EXIST.getCode(), "外部系统提供的展会ID不可重复");
            }
        }
        // 校验活动名称不能重复
        VenueBookAudit existByExhibitionName = getByExhibitionName(venueBookAuditDTO.getExhibitionName());
        if (existByExhibitionName != null) {
            throw BizException.wrap(VenueBookingExceptionCode.EX_NAME_EXIST.getCode(), "活动名称在场地预定申请中已存在");
        }
        // 展会编号不能重复
        if (StrUtil.isNotBlank(venueBookAuditDTO.getExhibitionNo())) {
            VenueBookAudit existByExhibitionNo = getByExhibitionNo(venueBookAuditDTO.getExhibitionNo());
            if (existByExhibitionNo != null) {
                throw BizException.wrap(VenueBookingExceptionCode.VENUE_BOOK_EXHIBITION_NO_EXIST.getCode(), "活动编号在场地预定申请中已存在");
            }
        }
        VenueBookAudit venueBookAudit = BeanUtil.toBean(venueBookAuditDTO, VenueBookAudit.class);
        return save(venueBookAudit);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateVenueBookAudit(VenueBookAuditDTO venueBookAuditDTO) {
        log.info("updateVenueBookAudit - 修改场馆预定审核, venueBookAuditDTO={}", venueBookAuditDTO);
        // 校验活动名称不能重复
        VenueBookAudit existByExhibitionName = getByExhibitionName(venueBookAuditDTO.getExhibitionName());
        if (existByExhibitionName != null && !existByExhibitionName.getId().equals(venueBookAuditDTO.getId())) {
            throw BizException.wrap(VenueBookingExceptionCode.EX_NAME_EXIST.getCode(), "活动名称在场地预定申请中已存在");
        }
        VenueBookAudit existByExhibitionNo = getByExhibitionNo(venueBookAuditDTO.getExhibitionNo());
        if (existByExhibitionNo != null && !existByExhibitionNo.getId().equals(venueBookAuditDTO.getId())) {
            throw BizException.wrap(VenueBookingExceptionCode.VENUE_BOOK_EXHIBITION_NO_EXIST.getCode(), "活动编号在场地预定申请中已存在");
        }
        // 尝试创建生成展会，商机，客户，联系人信息，验证能否通过校验
        trySaveExhibitionInfo(venueBookAuditDTO);
        VenueBookAudit venueBookAudit = BeanUtil.toBean(venueBookAuditDTO, VenueBookAudit.class);
        return updateById(venueBookAudit);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAuditStatus(VenueBookAuditDTO venueBookAuditDTO) throws Exception {
        log.info("updateAuditStatus- 更新审核结果, venueBookAuditDTO={}", venueBookAuditDTO);
        VenueBookAudit venueBookAudit = getById(venueBookAuditDTO.getId());
        if (venueBookAudit == null) {
            throw BizException.wrap(VenueBookingExceptionCode.VENUE_BOOK_NOT_EXIST.getCode(), "场馆预定申请不存在");
        }
        // 更新审核状态
        venueBookAudit.setAuditStatus(venueBookAuditDTO.getAuditStatus());
        venueBookAudit.setAuditOpinion(venueBookAuditDTO.getAuditOpinion());
        venueBookAudit.setAuditTime(LocalDateTime.now());
        venueBookAudit.setAuditUserId(venueBookAuditDTO.getAuditUserId());
        venueBookAudit.setAuditUserName(venueBookAuditDTO.getAuditUserName());
        updateById(venueBookAudit);
        // 审核通过，生成展会，商机，客户，联系人
        if (VenueBookAuditAuditStatusEnum.APPROVED.eq(venueBookAudit.getAuditStatus())) {
            saveExhibitionInfo(venueBookAudit);
            // 发送短信给联系人
            asyncSendAuditSmsMsg(venueBookAudit);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveExhibitionInfo(VenueBookAudit venueBookAudit) throws Exception {
        log.info("syncWebsiteExhibition - 对内门户场馆预定同步商机，展会，客户，联系人, websiteVenueBookDTO={}", venueBookAudit);
        checkVenueBookAuditParam(venueBookAudit);
        Map<String, Object> customerMainMap = customerMainService.saveForVenueBook(venueBookAudit);
        Long customerMainId = (Long) customerMainMap.get("customerMainId");
        String customerMainName = (String) customerMainMap.get("customerMainName");
        Long contactId = (Long) customerMainMap.get("contactId");
        String contactName = (String) customerMainMap.get("contactName");
        // 保存展会
        Long exhibitionId = exhibitionService.saveForVenueBook(venueBookAudit, customerMainId, customerMainName, contactId, contactName);
        // 保存商机
        businessOpportunityService.saveForVenueBook(venueBookAudit, customerMainId, customerMainName, contactId, contactName, exhibitionId);
        // 同步展会ID到展览会议系统
        if (StrUtil.isNotBlank(venueBookAudit.getExternalExhibitionCode())
                && VenueBookAuditVenueBookSourceEnum.CONFERENCE.eq(venueBookAudit.getVenueBookSource())) {
            meetAdService.syncExhCode(venueBookAudit.getExternalExhibitionCode(), exhibitionId + "");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void trySaveExhibitionInfo(VenueBookAudit venueBookAudit) {
        log.info("trySaveExhibitionInfo - 尝试创建生成展会，商机，客户，联系人信息, venueBookAudit={}", venueBookAudit);
        Map<String, Object> customerMainMap = customerMainService.saveForVenueBook(venueBookAudit);
        Long customerMainId = (Long) customerMainMap.get("customerMainId");
        String customerMainName = (String) customerMainMap.get("customerMainName");
        Boolean isNewCustomerMain = (Boolean) customerMainMap.get("isNewCustomerMain");
        Long contactId = (Long) customerMainMap.get("contactId");
        String contactName = (String) customerMainMap.get("contactName");
        // 保存展会
        Long exhibitionId = exhibitionService.saveForVenueBook(venueBookAudit, customerMainId, customerMainName, contactId, contactName);
        // 保存商机
        BusinessOpportunity businessOpportunity = businessOpportunityService.saveForVenueBook(venueBookAudit, customerMainId, customerMainName, contactId, contactName, exhibitionId);
        // 创建成功之后删掉数据，待审核通过之后创建数据，这么做的目的是使用创建业务数据的校验，校验方法只需要维护在业务保存方法中
        // 删除展会
        exhibitionService.removeById(exhibitionId);
        // 删除商机
        businessOpportunityService.removeById(businessOpportunity);
        // 删除新创建的用户和联系人
        if (isNewCustomerMain) {
            customerMainService.removeById(customerMainId);
            contactService.removeById(contactId);
            contactRefService.removeByContactId(contactId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void trySaveExhibitionInfo(VenueBookAuditDTO venueBookAuditDTO) {
        VenueBookAudit venueBookAudit = BeanUtil.toBean(venueBookAuditDTO, VenueBookAudit.class);
        trySaveExhibitionInfo(venueBookAudit);
    }

    @Override
    public boolean sendAuditSmsMsg(VenueBookAudit venueBookAudit) {
        log.info("sendAuditSmsMsg - 发送审核结果短信提醒, venueBookAudit={}", venueBookAudit);
        String mobile = venueBookAudit.getMobile();
        if (StrUtil.isBlank(mobile)) {
            log.info("sendAuditSmsMsg - 手机号缺失，无需发送");
            return false;
        }
        String content = "您所预定的活动，已通过审核，接下来会有销售专员联系您，请留意接听电话";
        //发送站内信
        SmsSendDTO smsSendDTO = new SmsSendDTO();
        smsSendDTO.setBusinessType(BusinessConstant.BUSINESS_COMMON);
        smsSendDTO.setChannelType(ChannelType.SMS);
        smsSendDTO.setBusinessId(ContextUtil.getTrace());
        smsSendDTO.setSystemScene(StaticContextUtil.getProjectSystemScene());
        // 封装内容
        Map<String, Object> contentMap = new HashMap<>(16);
        contentMap.put("content", content);
        SmsContentModel smsContentModel = new SmsContentModel();
        smsContentModel.setContentMap(contentMap);
        smsSendDTO.setSmsContentModelList(Collections.singletonList(smsContentModel));
        // 发送用户的手机号
        Set<String> mobileSet = new HashSet<>();
        mobileSet.add(mobile);
        smsSendDTO.setMobileList(mobileSet);
        // 发送时间
        smsSendDTO.setIsSendNow(BusinessConstant.YES);
        // 发送短信
        venueBookingMsgService.sendSmsMsg(smsSendDTO);
        return true;
    }

    @Override
    public void asyncSendAuditSmsMsg(VenueBookAudit venueBookAudit) {
        threadPoolTaskExecutor.execute(() -> sendAuditSmsMsg(venueBookAudit));
    }

    private QueryWrapper<VenueBookAudit> createVenueBookAuditVOQueryWrapper(VenueBookAuditDTO query) {
        QueryWrapper<VenueBookAudit> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .lambda()
                .eq(VenueBookAudit::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(StrUtil.isNotBlank(query.getExhibitionType()), VenueBookAudit::getExhibitionType, query.getExhibitionType())
                .eq(query.getAuditStatus() != null, VenueBookAudit::getAuditStatus, query.getAuditStatus())
                .and(query.getExhibitionStartTime() != null && query.getExhibitionEndTime() != null, exhibitionTimeQueryWrapper ->
                        exhibitionTimeQueryWrapper
                                .between(VenueBookAudit::getExhibitionStartTime, query.getExhibitionStartTime(), query.getExhibitionEndTime())
                                .or()
                                .between(VenueBookAudit::getExhibitionEndTime, query.getExhibitionStartTime(), query.getExhibitionEndTime()))
                .and(StrUtil.isNotBlank(query.getKeyword()), keywordQueryWrapper ->
                        keywordQueryWrapper
                                .like(VenueBookAudit::getExhibitionName, query.getKeyword())
                                .or()
                                .like(VenueBookAudit::getExhibitionShortName, query.getKeyword())
                                .or()
                                .like(VenueBookAudit::getCustomerCnName, query.getKeyword())
                                .or()
                                .like(VenueBookAudit::getContactName, query.getKeyword())
                                .or()
                                .like(VenueBookAudit::getMobile, query.getKeyword()))
        ;
        return queryWrapper;
    }

    /**
     * 校验场馆预定参数.
     *
     * @param venueBookAuditDTO the venue book audit dto
     */
    public void checkVenueBookAuditParam(VenueBookAuditDTO venueBookAuditDTO) {
        Set<ConstraintViolation<VenueBookAuditDTO>> errors;
        try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) {
            Validator validator = validatorFactory.getValidator();
            errors = validator.validate(venueBookAuditDTO, VenueBookAuditDTO.Save.class, VenueBookAuditDTO.Update.class);
        }
        if (errors == null || errors.isEmpty()) {
            return;
        }
        for (ConstraintViolation<VenueBookAuditDTO> error : errors) {
            throw BizException.wrap(ExceptionCode.BASE_VALID_PARAM.getCode(), error.getMessage());
        }
    }

    /**
     * 校验场馆预定参数.
     *
     * @param venueBookAudit the venue book audit
     */
    public void checkVenueBookAuditParam(VenueBookAudit venueBookAudit) {
        VenueBookAuditDTO venueBookAuditDTO = BeanUtil.toBean(venueBookAudit, VenueBookAuditDTO.class);
        checkVenueBookAuditParam(venueBookAuditDTO);
    }
}
